﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;

namespace KingOfCSharp.iHunter.Plugins.BBS
{
    public class TaskExecutor
    {
        enum State
        {
            NotConnected,
            Login,       
            MainMenu,
            Board,            
            EnteringBoard,
            ToMainMenu,
            PreCreatePost,
            EnteringHelpPage,
            EnteringSearchMode,
            SearchMode,
            ReadPost,
            ToBoardAfterWrongPost,
            ContinueReadPost,
            PreReplyPost,
            PreSendMail,
            EnteringMailbox,
            ReadMail,
            ContinueReadMail,
            BackToMailbox,
            PreReplyMail,
            SetMessageOption,
			SetMailboxOption
        }

        #region Public Properties
        private State CurState
        {
            get;
            set;
        }

        public BbsHost Host
        {
            get;
            private set;
        }

        public Terminal Term
        {
            get;
            private set;
        }

        #endregion

        #region Fields
        private HostConfigure configure;
        private Communicator communicator;

        
        
        private readonly Queue<Task> tasks;
        private List<TaskResult> results;
        private Task ActiveTask        
        {
            get
            {
                Monitor.Enter(tasks);
                Task t = null;
                if (tasks.Count > 0)
                    t = tasks.Peek();

                Monitor.Exit(tasks);
                return t;

                
            }
        }

        private int[] needUpdate;
        private string boardToEnter;
        private bool searchModeEntered = false;
        private int lastLine;
        private List<Post> postsDownloaded = null;
        private Post curPost = null;
        private DateTime postTime = new DateTime();
        private List<Mail> mailsDownloaded = null;
        private Mail curMail = null;
        private bool haveReadMail = false;
        private bool isMessageOptionSet = false;
        private string id;
        //private bool receiveExpired = false;
        private System.Threading.Timer receiveExpireTimer;
        private bool onError;
        private string lastIgnoredScreen;

        private static string dateTimeFormat = "MMM d HH:mm:ss yyyy";
        private static string mailTimeFormat = "MMM d";
        private static System.Globalization.CultureInfo formatProviderForDateTime = new System.Globalization.CultureInfo("en-US");
        private static int[] needUpdateLastLine = new int[] { -1 };
        private static int[] needUpdateAboveLast = new int[] { -2 };
        private static int[] needUpdateLastTwoLines = new int[] { -1, -2 };
        private static Regex regex_StatusBar = new Regex(@"时间\[.*\].*使用者\[(.*)\].*停留\[.*\]");
        private static Regex regex_ReadMode = new Regex(@"\[阅读文章\].*\^X或p");
        private static Regex regex_SameThreadMode = new Regex(@"\[主题阅读\]");
        private static Regex regex_ToBeContinued = new Regex(@"下面还有喔 \((\d+)%\).+开头末尾\|");
        private static Regex regex_EnterContinue = new Regex("请按.*Enter.*继续");
        private static Regex regex_Post_Author = new Regex(@"发信人: (\w+) \(.*\), 信区: (\w+)");
        private static Regex regex_Post_Title = new Regex(@"标  题: (.*)");
        private static Regex regex_Post_Time = new Regex(@"发信站: ([\w ]+) \(\w{3} (\w{3} [ 0-9]{2} \d{2}:\d{2}:\d{2} \d{4})\)");
        private static Regex regex_CurrentPostThumbnail = new Regex(@">\s*(\d+)");
        private static Regex regex_Reply = new Regex("T改标题");
        private static Regex regex_MessageBoard = new Regex(@"W\. 进站时观看留言版\s+(\w+)");
        private static Regex regex_MailOperationBar = new Regex(@"\(R\)回信, \(D\)删除, \(G\)继续\? \[\w\]:");
        private static Regex regex_Mail_Sender = new Regex(@"寄信人: (\w+)");
        private static Regex regex_MailInfo = new Regex(@">\s*(\d+).{4}(\w+)\s+(\w{3} [\d ]\d)\s+(.*)");
        private static Regex regex_AcceptMsgsFromAll = new Regex(@"P\. 接受所有人的讯息\s+(\w+)");
        private static Regex regex_AcceptMsgsFromAFriends = new Regex(@"Q\. 接受好友的讯息\s+(\w+)");
		private static Regex regex_SaveSentMailsToOutbox = new Regex(@"A\. 发信时保存信件到发件箱\s+(\w+)");
        private static string cmd_End = Character.GetControlString(System.Windows.Forms.Keys.End, System.Windows.Forms.Keys.None);
        private static string cmd_Down = Character.GetControlString(System.Windows.Forms.Keys.Down, System.Windows.Forms.Keys.None);
        private static string cmd_Up = Character.GetControlString(System.Windows.Forms.Keys.Up, System.Windows.Forms.Keys.None);
        private static string cmd_Home = Character.GetControlString(System.Windows.Forms.Keys.Home, System.Windows.Forms.Keys.None);
        private static string cmd_EndPost = Character.GetControlString(System.Windows.Forms.Keys.W, System.Windows.Forms.Keys.Control);
        private static string cmd_Post = Character.GetControlString(System.Windows.Forms.Keys.P, System.Windows.Forms.Keys.Control);
        private static string cmd_Right = Character.GetControlString(System.Windows.Forms.Keys.Right, System.Windows.Forms.Keys.None);
        private static string cmd_Left = Character.GetControlString(System.Windows.Forms.Keys.Left, System.Windows.Forms.Keys.None);
        private static SetMessageOptionTask setMsgOptTask = new SetMessageOptionTask(false);
		private static SetMailboxOptionTask setMailOptTask = new SetMailboxOptionTask();

        // ToDo: Remove these fields when releasing
        // For debug only
        private string lastSent = "";
		private string lastReceived = "";
        private static Exception bugFound = new Exception("Bug in BBSClient");
        #endregion

        public delegate void TaskCompletedEventHandler(object sender, TaskCompletedEventArgs args);

        public event TaskCompletedEventHandler TaskCompletedHandler;

        public TaskExecutor(BbsHost host)
        {
            this.Host = host;
            this.configure = HostConfigureFactory.GetHostConfigure(host.HostType);
            this.CurState = State.NotConnected;
            this.tasks = new Queue<Task>(); 
            this.results = new List<TaskResult>();
            this.needUpdate = null;
            this.boardToEnter = "";

            this.id = host.Username;

            this.communicator = new Communicator(Host.HostAddress, Host.Username, Host.Password, Host.ConnectionType, Host.Port);
            
            this.Term = new Terminal();
            this.lastLine = Term.RowNum - 1;

            communicator.DataReceivedHandler += new Communicator.DataReceivedEventHandler(OnReceiveData);
            communicator.ErrorHandler += new Communicator.ErrorEventHandler(OnError);

            this.isMessageOptionSet = false;
            //this.receiveExpired = false;
            this.receiveExpireTimer = new System.Threading.Timer(new TimerCallback(OnReceiveTimeout));

            this.lastIgnoredScreen = string.Empty;

            this.onError = false;
        }

        private void OnReceiveTimeout(object argNotUsed)
        {
            OnError(this, new System.Net.WebException("Receive timeout"));
        }

        // ToDo : when error occurs
        private void OnError(object sender, Exception exception)
        {
            System.Diagnostics.Debug.WriteLine("Error start");

            System.Diagnostics.Debug.WriteLine("\nCurrent Term State:\n" + Term.ToText());
            System.Diagnostics.Debug.WriteLine("\nLast Sent: " + lastSent);
            System.Diagnostics.Debug.WriteLine("\nLast Receiver: " + lastReceived);

            onError = true;

            receiveExpireTimer.Change(Timeout.Infinite, Timeout.Infinite);

            CurState = State.NotConnected;

            ClearTaskQueue(exception);

            Disconnect();

            onError = false;
            System.Diagnostics.Debug.WriteLine("Error end");
        }

        private void ClearTaskQueue(Exception exp)
        {           

            Monitor.Enter(tasks);

            while (tasks.Count > 0)
            {
                Task task = this.tasks.Dequeue();

                if (this.TaskCompletedHandler != null)
                {
                    object result = null;
                    switch (task.TaskType)
                    {
                        case Task.Type.DownloadPosts:
                            result = postsDownloaded;
                            break;

                        case Task.Type.DownloadMails:
                            result = mailsDownloaded;
                            break;

                        case Task.Type.SendMail:
                        case Task.Type.CreateThread:
                        case Task.Type.ReplyMail:
                        case Task.Type.ReplyPost:
                            result = false;
                            break;
                    }

                    switch (task.TaskType)
                    {
                        case Task.Type.DownloadMails:
                        case Task.Type.DownloadPosts:
                        case Task.Type.SendMail:
                        case Task.Type.CreateThread:
                        case Task.Type.ReplyMail:
                        case Task.Type.ReplyPost:
                            this.TaskCompletedHandler(this, new TaskCompletedEventArgs(
                                task,
                                false,
                                exp,
                                result,
                                true
                                ));
                            break;
                    }

                }
            }

            
            
            Monitor.Exit(tasks);       

            
        }
        
        public void Disconnect()
        {
            onError = true;

            isMessageOptionSet = false;

            Monitor.Enter(communicator);
            if (CurState != State.NotConnected)
            {
                CurState = State.NotConnected;

                communicator.Disconnect();
            }

            Monitor.Exit(communicator);

            //ignoreReceive = false;
        }

        // only for debug
        public Communicator GetCommunicator()
        {
            return communicator;

            // return null;
        }

        public void ExecuteTask(Task task)
        {
            Debug.Assert(null != task);

            //System.Diagnostics.Debug.WriteLine("Execute task");

            Monitor.Enter(tasks);

            if ((!isMessageOptionSet) && (!tasks.Contains(setMsgOptTask)))
            {
                Queue<Task> temp = new Queue<Task>();
                temp.Enqueue(setMsgOptTask);
				temp.Enqueue(setMailOptTask);

                while (tasks.Count > 0)
                {
                    temp.Enqueue(tasks.Dequeue());
                }

                tasks.Clear();

                foreach (Task t in temp)
                {
                    tasks.Enqueue(t);
                }
            }

            this.tasks.Enqueue(task);
            Monitor.Exit(tasks);

            if (CurState == State.NotConnected)
            {
                CurState = State.Login;
                System.Threading.Thread.Sleep(3000);

                communicator.Connect();
                
                //return;
            }
            else if (CurState == State.MainMenu)
                OperateOnMainMenu();
        }

      
        private void OnReceiveData(object sender, DataReceivedEventArgs args)
        {
            receiveExpireTimer.Change(Timeout.Infinite, Timeout.Infinite);

            if (this.onError)
                return;

			lastReceived = System.Text.Encoding.GetEncoding("gb2312").GetString(args.Data, 0, args.Data.Length);

            Term.Write(args.Data);

            //System.Diagnostics.Debug.WriteLine(Term.ToText());

            this.CheckState();
        }

        
        private void OperateOnMainMenu()
        {
            Debug.Assert(CurState == State.MainMenu);                

            Task activeTask = ActiveTask;

            if (null == activeTask)
            {
                // ToDo : when tasks are all done
            }
            else
            {
                switch (activeTask.TaskType)
                {
                    case Task.Type.CreateThread:
                        {
                            CreateThreadTask task = (CreateThreadTask)activeTask;

                            CurState = State.EnteringBoard;
                            needUpdate = needUpdateAboveLast;
                            boardToEnter = task.Board;

                            Send(configure.MainMenuToBoard + task.Board + "\r" + cmd_End + cmd_Down + cmd_Down + cmd_End);
                        }
                        break;
                    case Task.Type.DownloadPosts:
                        {
                            DownloadPostsTask task = (DownloadPostsTask)activeTask;

                            CurState = State.EnteringBoard;
                            if (!Host.Username.ToLower().Trim().Equals("guest"))
                                needUpdate = needUpdateAboveLast;
                            else
                                needUpdate = null;
                            boardToEnter = task.Board;

                            Send(configure.MainMenuToBoard + task.Board + "\r" + cmd_End + cmd_Down + cmd_Down + cmd_End);
                        }
                        break;

                    case Task.Type.ReplyPost:
                        {
                            ReplyPostTask task = activeTask as ReplyPostTask;

                            CurState = State.EnteringBoard;
                            needUpdate = needUpdateAboveLast;
                            boardToEnter = task.Board;

                            Send(configure.MainMenuToBoard + task.Board + "\r" + cmd_End + cmd_Down + cmd_Down + cmd_End);
                        }
                        break;

                    case Task.Type.SendMail:
                        {
                            if (HaveMailbox())
                            {
                                CurState = State.PreSendMail;

                                Send(configure.PreSendMailCommand + (activeTask as SendMailTask).Receiver + "\r");
                            }
                            else
                            {
								FinishCurrentTask(false, new InvalidOperationException("You do not have a mailbox"), false);
                            }
                        }
                        break;

                    case Task.Type.DownloadMails:
                        {
                            if (HaveMailbox())
                            {
                                this.mailsDownloaded = new List<Mail>();
                                CurState = State.EnteringMailbox;

								DownloadMailsTask task = ActiveTask as DownloadMailsTask;

								if (task.Folder == DownloadMailsTask.MailFolder.Outbox)
									Send(configure.MainMenuToSentMailbox + cmd_End);
								else
	                                Send(configure.MainMenuToMailbox + cmd_End);
                            }
                            else
                            {
								FinishCurrentTask(false, new InvalidOperationException("You do not have a mailbox"), false);
                            }
                        }
                        break;

                    case Task.Type.ReplyMail:
                        {
                            if (HaveMailbox())
                            {
                                ReplyMailTask task = activeTask as ReplyMailTask;
                                Debug.Assert(null != task.MailToReply);

                                if (task.MailToReply.Type == MailType.Message)
                                {
									FinishCurrentTask(false, new InvalidOperationException("Can not reply a mail of this type"), false);
                                    return;
                                }
                                else
                                {
                                    CurState = State.EnteringMailbox;
                                    Send(configure.MainMenuToMailbox + cmd_End);
                                }
                            }
                            else
                            {
								FinishCurrentTask(false, new InvalidOperationException("You do not have a mailbox"), false);
                            }
                        }
                        break;

                    case Task.Type.SetMessageOption:
                        {
                            if (Host.Username.ToLower().Trim().Equals("guest"))
                            {
                                FinishCurrentTask(true, null, false);
                            }
                            else
                            {
                                CurState = State.SetMessageOption;

                                Send("i\ru\r");
                            }
                        }
                        break;

					case Task.Type.SetMailboxOption:
						{
                            if (Host.Username.ToLower().Trim().Equals("guest"))
                            {
                                FinishCurrentTask(true, null, false);
                            }
                            else
                            {
                                CurState = State.SetMailboxOption;

                                Send("m\rx\r");
                            }
						}
						break;

                    case Task.Type.Logout:
                        {
                            Send("g\r\r\r");

                            CurState = State.NotConnected;

							FinishCurrentTask(true, null, false);

                            
                        }
                        break;
                }
            }
        }

        private bool HaveMailbox()
        {
            Debug.Assert(CurState == State.MainMenu);

            string[] lines = Term.GetLines();            

            foreach (string line in lines)
            {
                if (configure.MailboxEntry.Match(line).Success)
                {
                    return true;
                }
            }

            return false;
        }

        private void OperateOnBoard()
        {
            Debug.Assert(CurState == State.Board);                

            Task activeTask = ActiveTask;

            switch (activeTask.TaskType)
            {
                case Task.Type.CreateThread:
                    CurState = State.PreCreatePost;
                    needUpdate = needUpdateLastLine;

                    Send(cmd_Post);

                    break;
                case Task.Type.DownloadPosts:
                case Task.Type.ReplyPost:
                    CurState = State.EnteringHelpPage;
                    needUpdate = needUpdateLastLine;
                    searchModeEntered = false;
                    this.postsDownloaded = new List<Post>();

                    Send("h");
                    break;
            }
        }

        private void FinishCurrentTask(bool success, Exception err, bool retry)
        {
            if (success)
            {
                if (ActiveTask == setMsgOptTask)
                {
                    isMessageOptionSet = true;
                }

                if (ActiveTask.TaskType == Task.Type.Logout)
                {
                    isMessageOptionSet = false;
                }
            }

            Monitor.Enter(tasks);
            Debug.Assert(tasks.Count > 0);
            Task task = this.tasks.Dequeue();
            Monitor.Exit(tasks);

            if (this.TaskCompletedHandler != null)
            {
                object result = null;
				switch (task.TaskType)
				{
					case Task.Type.DownloadPosts:
                        if (success)
                        {
                            result = postsDownloaded;
                        }
                        else
                        {
                            result = null;
                        }
						break;

					case Task.Type.DownloadMails:
                        if (success)
                        {
                            result = mailsDownloaded;
                        }
                        else
                        {
                            result = null;
                        }
						break;

					case Task.Type.SendMail:
					case Task.Type.CreateThread:
					case Task.Type.ReplyMail:
					case Task.Type.ReplyPost:
						result = success;
						break;
				}

				switch (task.TaskType)
				{
					case Task.Type.DownloadMails:
					case Task.Type.DownloadPosts:
					case Task.Type.SendMail:
					case Task.Type.CreateThread:
					case Task.Type.ReplyMail:
					case Task.Type.ReplyPost:
						this.TaskCompletedHandler(this, new TaskCompletedEventArgs(
							task,
							success,
							err,
							result,
							retry
							));
						break;
				}
                
            }

            if (CurState == State.MainMenu)
            {
                OperateOnMainMenu();
            }
            
            
        }

        private void OnStateLogin()
        {
            string lastLine = Term.GetLine(Term.RowNum - 1);
            Match m = regex_StatusBar.Match(lastLine);
            if (m.Success)
            {
                string topLine = Term.GetLine(0);
                if (topLine.Contains("主选单") && Term.CurrentCursor.X == 1)
                {
                    this.id = m.Groups[1].Value;

                    CurState = State.MainMenu;
                    OperateOnMainMenu();
                    return;
                }
                else if (topLine.StartsWith("[好朋友列表]"))
                {
                    System.Diagnostics.Debug.WriteLine("Pre MainMenu:\n" + Term.ToText());

                    Send("e");
                    return;
                }
            }


            string line = Term.GetLines(Term.CurrentCursor.X, Term.CurrentCursor.X)[0];
            

            if (line.StartsWith("请输入代号: "))
            {
                string upperLine = Term.GetLine(Term.CurrentCursor.X - 2);
                if (upperLine.Contains("错误的使用者代号"))
                {
                    //Disconnect();
                    OnError(this, new InvalidOperationException("Username does not exist"));
                }
                else if (upperLine.Contains("密码输入错误"))
                {
                    //Disconnect();
                    OnError(this, new InvalidOperationException("Incorrect password"));
                }

                Send(this.Host.Username + "\r");
                return;
            }
            else if (line.StartsWith("请输入密码: "))
            {
                Send(this.Host.Password + "\r");
                return;
            }

            foreach (ExceptionalQuestion ques in configure.Questions)
            {
                if (ques.Question.Match(line).Success)
                {
                    string screen;

                    if (Term.CurrentCursor.X == 0)
                        screen = Term.GetLine(0);
                    else
                    {
                        screen = Term.ToText(0, Term.CurrentCursor.X - 1);
                    }

                    if (lastIgnoredScreen.Equals(screen))
                        return;

                    lastIgnoredScreen = screen;

                    Send(ques.Operation);
                    return;
                }
            }
        }

        private void OnStateEnteringBoard()
        {
            string line = Term.GetLine(2);
            if (line.Contains("不正确的讨论区") || line.Contains("错误的讨论区"))
            {
                CurState = State.ToMainMenu;
				this.FinishCurrentTask(false, new InvalidOperationException("Incorrect board"), false);
                Send("\rq");
                return;
            }

            line = Term.GetLine(0);
            if (!line.ToLower().EndsWith("[" + boardToEnter.ToLower() + "]"))
            {
                CurState = State.ToMainMenu;
				this.FinishCurrentTask(false, new InvalidOperationException("Incorrect board"), false);
                Send(configure.BoardToMainMenu);
                return;
            }
            else
            {
                CurState = State.Board;
                OperateOnBoard();
                return;
            }
        }

        private void OnStatePreCreatePost()
        {
            string line = Term.GetLine(Term.RowNum - 1);
            if (regex_EnterContinue.Match(line).Success)
            {
                CurState = State.ToMainMenu;

				this.FinishCurrentTask(false, new InvalidOperationException("Unable to post"), false);
                Send("\r" + configure.BoardToMainMenu);
                return;
            }
            else
            {
                CurState = State.ToMainMenu;

                CreateThreadTask task = (CreateThreadTask)this.ActiveTask;
                Send(task.Title + "\r\r" + task.Content + cmd_EndPost + "\r" + configure.BoardToMainMenu);

				this.FinishCurrentTask(true, null, false);
                return;
            }
        }

        private void OnStateEnteringHelpPage()
        {
            if (!(Term.CurrentCursor.X == lastLine && regex_ToBeContinued.Match(Term.GetLine(lastLine)).Success))
                return;

            Task activeTask = ActiveTask;
            if (null == activeTask)
                OnError(this, new InvalidOperationException("Bug found in BBS Client"));

            string cmd = "";
            if (activeTask.TaskType == Task.Type.DownloadPosts)
            {
                DownloadPostsTask task = (activeTask as DownloadPostsTask);
                cmd = task.SearchCommand;
                this.postTime = task.StartFrom;
            }
            else if (activeTask.TaskType == Task.Type.ReplyPost)
            {
                ReplyPostTask task = (activeTask as ReplyPostTask);
                cmd = task.SearchCommand;
                this.postTime = task.PostToReply.PostTime;
            }
            else
                OnError(this, new InvalidOperationException("Bug found in BBS Client"));

            if (!searchModeEntered)
            {
                CurState = State.EnteringSearchMode;
                needUpdate = needUpdateLastLine;
                string content = string.Format(
                    "qq{0}7\r{1}\r",
                    Character.GetControlString(System.Windows.Forms.Keys.G, System.Windows.Forms.Keys.Control),
                    cmd
                    );
                Send(content);
            }
            else
            {
                CurState = State.SearchMode;
                needUpdate = needUpdateLastLine;

                Send("qq" + cmd_End);
            }
        }

        private void OnStateEnteringSearchMode()
        {
            string line = Term.GetLine(3);
            if (line.StartsWith("表达式错误"))
            {
                OnError(this, new InvalidOperationException("Bug found in BBS Client"));
            }

            if (line.StartsWith("一个都没有找到"))
            {
                CurState = State.ToMainMenu;
				FinishCurrentTask(false, new InvalidOperationException("Unable to find required posts"), false);
                Send(configure.BoardToMainMenu);
                return;
            }
            else
            {
                searchModeEntered = true;
                CurState = State.EnteringHelpPage;
                needUpdate = needUpdateLastLine;

                Send("h");
                return;
            }
        }

        private void OnStateToBoardAfterWrongPost()
        {
            if (!Term.Updated[lastLine] || Term.CurrentCursor.X < 3 ||
                            Term.CurrentCursor.X >= lastLine)
                return;

            string bottom = Term.GetLine(lastLine);
            if (!(regex_StatusBar.Match(bottom).Success))
                return;

            string line = Term.GetLine(Term.CurrentCursor.X);
            Match m = regex_CurrentPostThumbnail.Match(line);
            if (m.Success && int.Parse(m.Groups[1].Value) > 1)
            {
                CurState = State.ReadPost;
                Send(cmd_Up + "\r");
            }
            else
            {
                OnReachPostListTop();
            }
        }

        private void OnStateReadPost()
        {
            string line = Term.GetLine(lastLine);

            if (regex_StatusBar.Match(line).Success && Term.Updated[3])
            {
                string thirdLine = Term.GetLine(3);
                Match m = regex_CurrentPostThumbnail.Match(thirdLine);
                if (m.Success && int.Parse(m.Groups[1].Value) <= 1)
                {
                    OnReachPostListTop();
                }
            }

            

            if ((!Term.Updated[lastLine]) || Term.CurrentCursor.X != lastLine)
                return;

            int cursorY = 0;
            Match match_ToBeCont = regex_ToBeContinued.Match(line);
            bool toBeCont = match_ToBeCont.Success;
            if (toBeCont)
                cursorY = Character.GetStringLengthInTerm(match_ToBeCont.Groups[0].Value);
            else
                toBeCont =  (line.StartsWith("下面还有喔") && Term.CurrentCursor.Y >= Term.ColNum-1);
            Match match_ReadMode = regex_ReadMode.Match(line);
            bool readmode = match_ReadMode.Success;
            if (readmode)
                cursorY = Character.GetStringLengthInTerm(match_ReadMode.Groups[0].Value);


            if (!((toBeCont || readmode) && Term.CurrentCursor.Y >= cursorY))
                return;

            if (ActiveTask.TaskType == Task.Type.DownloadPosts)
            {
                if (readmode)
                {
                    if (IsPostValid())
                    {
                        if (curPost.PostTime.CompareTo(postTime) < 0)
                        {
                            OnReachPostListTop();
                            return;
                        }

                        curPost.HtmlText = Term.ToHtml(0, lastLine - 1);
                        curPost.PlainText = Term.ToText(0, lastLine - 1);
                        this.postsDownloaded.Add(curPost);

                        if (this.ItemDownloadedEventHandler != null)
                            this.ItemDownloadedEventHandler(this, new ItemDownloadedEventArgs(curPost));

                        CurState = State.ReadPost;
                        Send(cmd_Up);
                    }
                    else
                    {
                        CurState = State.ToBoardAfterWrongPost;
                        needUpdate = needUpdateLastLine;
                        Send("q");
                    }
                }
                else if (toBeCont)
                {
                    if (IsPostValid())
                    {
                        if (curPost.PostTime.CompareTo(postTime) < 0)
                        {
                            OnReachPostListTop();
                            return;
                        }

                        curPost.HtmlText = Term.ToHtml(0, lastLine - 1);
                        curPost.PlainText = Term.ToText(0, lastLine - 1);

                        CurState = State.ContinueReadPost;
                        Send(cmd_Down);
                    }
                    else
                    {
                        CurState = State.ToBoardAfterWrongPost;
                        needUpdate = needUpdateLastLine;
                        Send("qq");
                    }
                }
                else
                    OnError(this, new InvalidOperationException("Bug found in BBS Client"));
            }
            else if (ActiveTask.TaskType == Task.Type.ReplyPost)
            {
                if (readmode)
                {
                    if (IsPostValid())
                    {
                        if (curPost.PostTime.CompareTo(postTime) < 0)
                        {
                            OnReachPostListTop();
                            return;
                        }
                        else
                        {
                            ReplyPostTask task = ActiveTask as ReplyPostTask;

                            if (curPost.PostTime.CompareTo(task.PostToReply.PostTime) == 0 
                                && task.PostToReply.Author.Equals(curPost.Author))
                            {
                                CurState = State.PreReplyPost;
                                Send(cmd_Left+"q"+cmd_Right+"r");
                            }
                            else
                            {
                                Send(cmd_Up);
                            }
                        }
                    }
                    else
                    {
                        CurState = State.ToBoardAfterWrongPost;
                        needUpdate = needUpdateLastLine;
                        Send("q");
                    }
                }
                else if (toBeCont)
                {
                    if (IsPostValid())
                    {
                        if (curPost.PostTime.CompareTo(postTime) < 0)
                        {
                            OnReachPostListTop();
                            return;
                        }
                        else
                        {
                            ReplyPostTask task = ActiveTask as ReplyPostTask;

                            if (curPost.PostTime.CompareTo(task.PostToReply.PostTime) == 0
                                && task.PostToReply.Author.Equals(curPost.Author))
                            {
                                CurState = State.PreReplyPost;
                                Send(cmd_Left + cmd_Left + "q" + cmd_Right + "r");
                            }
                            else
                            {
                                Send(cmd_Up);
                            }
                        }
                    }
                    else
                    {
						CurState = State.ToBoardAfterWrongPost;
						needUpdate = needUpdateLastLine;
						Send("qq");
                    }
                }
                else
                    OnError(this, new InvalidOperationException("Bug found in BBS Client"));
            }
            else
                OnError(this, new InvalidOperationException("Bug found in BBS Client"));
        }

        private void OnStateSearchMode()
        {
            if (!(Term.CurrentCursor.X > 2 && Term.CurrentCursor.X < lastLine && regex_StatusBar.Match(Term.GetLine(lastLine)).Success))
                return;

            CurState = State.ReadPost;
            needUpdate = needUpdateLastLine;

            Send("\r");
        }

        private void OnStateContinueReadPost()
        {
            if ((!Term.Updated[lastLine]) || (!Term.Updated[lastLine-1]) || Term.CurrentCursor.X != lastLine)
                return;

            string line = Term.GetLine(lastLine);

            int cursorY = 0;
            Match match_ToBeCont = regex_ToBeContinued.Match(line);
            bool toBeCont = match_ToBeCont.Success;
            if (toBeCont)
                cursorY = Character.GetStringLengthInTerm(match_ToBeCont.Groups[0].Value);
            else
                toBeCont = (line.StartsWith("下面还有喔") && Term.CurrentCursor.Y >= Term.ColNum - 1);

            Match match_ReadMode = regex_ReadMode.Match(line);
            bool readmode = match_ReadMode.Success;
            if (readmode)
                cursorY = Character.GetStringLengthInTerm(match_ReadMode.Groups[0].Value);


            if (!((toBeCont || readmode) && Term.CurrentCursor.Y >= cursorY))
                return;

            curPost.HtmlText += Term.ToHtml(lastLine - 1, lastLine - 1);
            curPost.PlainText += Term.ToText(lastLine - 1, lastLine - 1);            

            if (toBeCont)
            {
                Send(cmd_Down);
            }
            else if (readmode)
            {
                this.postsDownloaded.Add(curPost);

                if (this.ItemDownloadedEventHandler != null)
                    this.ItemDownloadedEventHandler(this, new ItemDownloadedEventArgs(curPost));

                CurState = State.ReadPost;

                Send(cmd_Up);
            }
//             else
//                 OnError(this, new Exception("Bug found in BBS Client"));
        }

        private void OnStatePreReplyPost()
        {
            if (Term.CurrentCursor.X != lastLine)
                return;

            string line = Term.GetLine(lastLine);

            if (regex_EnterContinue.Match(line).Success)
            {
                CurState = State.ToMainMenu;
				FinishCurrentTask(false, new InvalidOperationException("Unable to post"), false);

                Send("\rq"+configure.BoardToMainMenu);
            }
            else if (regex_Reply.Match(line).Success)
            {
                CurState = State.ToMainMenu;
                string cmd = (ActiveTask as ReplyPostTask).ReplyCommand + configure.BoardToMainMenu;                

                Send(cmd);

				FinishCurrentTask(true, null, false);
            }
        }

        private void OnStatePreSendMail()
        {
            if (!(Term.Updated[lastLine - 1]))
                return;

            string line = Term.GetLine(0);
            if (line.StartsWith("[处理信笺选单]"))
            {
                CurState = State.ToMainMenu;

                FinishCurrentTask(false, 
                    new InvalidOperationException("Unable to send, maybe the receiver does not exist"),
                    false);

                Send(configure.PostSendMailCommand);
            }
            else if (line.StartsWith("收信者不正确"))
            {
                CurState = State.ToMainMenu;

				FinishCurrentTask(false, 
                    new InvalidOperationException("The receiver does not exist"),
                    false);

                Send("\r"+configure.PostSendMailCommand);
            }
            else
            {
                if (Term.GetLine(lastLine).StartsWith("标题:") && Term.CurrentCursor.X == lastLine
                    && Term.CurrentCursor.Y >= 6)
                {
                    CurState = State.ToMainMenu;

                    SendMailTask task = ActiveTask as SendMailTask;
                    string cmd = task.Title+"\r\r"+task.Content+cmd_EndPost+"\r\r\r"+configure.PostSendMailCommand;

                    Send(cmd);

					FinishCurrentTask(true, null, false);

                    
                }
            }
        }

        private void OnStateEnteringMailbox()
        {
            if (!(Term.Updated[lastLine - 1] && Term.CurrentCursor.X > 2
                && Term.CurrentCursor.X < lastLine && Term.CurrentCursor.Y == 1))
                return;

            if (Term.GetLine(Term.CurrentCursor.X)[0] != '>')
            {
                //CurState = State.ToMainMenu;

                //FinishCurrentTask(true, "收件箱中没有邮件");


                //Send(configure.MailboxToMainMenu);
                OnReachMailboxTop();
            }
            else
            {
                CurState = State.ReadMail;
                haveReadMail = false;

                Send("\r");
            }
        }

        private void OnStateReadMail()
        {
            if (haveReadMail)
            {
                if (regex_StatusBar.Match(Term.GetLine(lastLine)).Success &&
                    Term.CurrentCursor.X == 3 && Term.CurrentCursor.Y == 1)
                    OnReachMailboxTop();
            }

            if (!(Term.CurrentCursor.X == lastLine && Term.Updated[lastLine]))
                return;

            int cursorY = 0;
            Match match_ToBeCont = regex_ToBeContinued.Match(Term.GetLine(lastLine));
            bool toBeCont = match_ToBeCont.Success;
            if (toBeCont)
                cursorY = Character.GetStringLengthInTerm(match_ToBeCont.Groups[0].Value);
            Match match_mailBar = regex_MailOperationBar.Match(Term.GetLine(lastLine));
            bool mailOpBar = match_mailBar.Success;
            if (mailOpBar)
                cursorY = Character.GetStringLengthInTerm(match_mailBar.Groups[0].Value);

            
            if (!((toBeCont|| mailOpBar) && Term.CurrentCursor.Y >= cursorY))
                return;

            haveReadMail = true;

            switch (ActiveTask.TaskType)
            {
                case Task.Type.DownloadMails:
                    {
                        JudgeMailType();
                        curMail.HtmlText = Term.ToHtml(0, lastLine - 1);
                        curMail.PlainText = Term.ToText(0, lastLine - 1);

                        if (mailOpBar)
                        {
                            if (curMail.Type == MailType.NormalMail)
                            {
                                if (curMail.SendTime.CompareTo((ActiveTask as DownloadMailsTask).StartFrom) < 0)
                                {
                                    OnReachMailboxTop();
                                    return;
                                }

                                mailsDownloaded.Add(curMail);

                                if (this.ItemDownloadedEventHandler != null)
                                    this.ItemDownloadedEventHandler(this, new ItemDownloadedEventArgs(curMail));

                                Send(cmd_Up);
                            }
                            else
                            {
                                CurState = State.BackToMailbox;
                                Send(cmd_Left);
                            }
                        }
                        else
                        {
                            CurState = State.ContinueReadMail;
                            Send(cmd_Down);
                        }
                    }
                    break;

                case Task.Type.ReplyMail:
                    {
                        JudgeMailType();

                        ReplyMailTask task = ActiveTask as ReplyMailTask;
                        if (curMail.Type == MailType.NormalMail)
                        {
                            
                            int timeComp = curMail.SendTime.CompareTo(task.MailToReply.SendTime);

                            if (timeComp < 0)
                            {
                                OnReachMailboxTop();
                                return;
                            }

                            if (timeComp == 0 &&
                                curMail.Sender == task.MailToReply.Sender &&
                                curMail.Title == task.MailToReply.Title)
                            {
                                CurState = State.PreReplyMail;

                                if (toBeCont)
                                    Send("rr");
                                else
                                    Send("r");

                                return;
                            }
                            else
                            {
                                if (toBeCont)
                                    Send(cmd_Up + cmd_Up);
                                else
                                    Send(cmd_Up);

                                return;
                            }
                        }
                        else
                        {
                            string[] lines = task.MailToReply.PlainText.Split(
                                new char[] { '\n' });

                            Debug.Assert(null != lines && lines.Length > 3);

                            if (lines[0] == Term.GetLine(0) &&
                                lines[1] == Term.GetLine(1) &&
                                lines[2] == Term.GetLine(2))
                            {
                                CurState = State.PreReplyMail;

                                if (toBeCont)
                                    Send("rr");
                                else
                                    Send("r");

                                return;
                            }
                            else
                            {
                                CurState = State.BackToMailbox;

                                if (toBeCont)
                                    Send(cmd_Left + cmd_Left);
                                else
                                    Send(cmd_Left);
                                return;
                            }
                        }
                    }
                    // break;
                    // All control paths ended with return, 'break' is not needed
            }
        }

        private void OnStateBackToMailbox()
        {
            if (!(regex_StatusBar.Match(Term.GetLine(lastLine)).Success
                && Term.CurrentCursor.Y == 1))
                return;

            Match m = regex_MailInfo.Match(Term.GetLine(Term.CurrentCursor.X));

            Debug.Assert(m.Success);

            curMail.Sender = m.Groups[2].Value;
            curMail.Title = m.Groups[4].Value.TrimEnd();
			if (curMail.Title == "")
				curMail.Title = " ";
            curMail.SendTime = DateTime.ParseExact(m.Groups[3].Value, mailTimeFormat,
                formatProviderForDateTime, System.Globalization.DateTimeStyles.AllowInnerWhite);


            if (ActiveTask.TaskType == Task.Type.DownloadMails)
            {
                DownloadMailsTask task = ActiveTask as DownloadMailsTask;
                if (curMail.SendTime.AddDays(1).CompareTo(task.StartFrom) >= 0)
                {
                    mailsDownloaded.Add(curMail);

                    if (this.ItemDownloadedEventHandler != null)
                        this.ItemDownloadedEventHandler(this, new ItemDownloadedEventArgs(curMail));
                }
            }

            int ind = int.Parse(m.Groups[1].Value);
            if (ind == 1)
                OnReachMailboxTop();
            else
            {
                CurState = State.ReadMail;
                Send(cmd_Up + "\r");
            }
        }

        private void OnStateContinueReadMail()
        {
            if ((!Term.Updated[lastLine]) || (!Term.Updated[lastLine - 1]) || Term.CurrentCursor.X != lastLine)
                return;

            int cursorY = 0;
            Match match_ToBeCont = regex_ToBeContinued.Match(Term.GetLine(lastLine));
            bool toBeCont = match_ToBeCont.Success;
            if (toBeCont)
                cursorY = Character.GetStringLengthInTerm(match_ToBeCont.Groups[0].Value);
            Match match_mailBar = regex_MailOperationBar.Match(Term.GetLine(lastLine));
            bool mailOpBar = match_mailBar.Success;
            if (mailOpBar)
                cursorY = Character.GetStringLengthInTerm(match_mailBar.Groups[0].Value);

            if (!((toBeCont || mailOpBar) && Term.CurrentCursor.Y >= cursorY))
                return;

            if (toBeCont)
            {
                curMail.HtmlText += Term.ToHtml(lastLine - 1, lastLine - 1);
                curMail.PlainText += Term.ToText(lastLine - 1, lastLine - 1);

                Send(cmd_Down);
            }
            else if (mailOpBar)
            {
                curMail.HtmlText += Term.ToHtml(lastLine - 1, lastLine - 1);
                curMail.PlainText += Term.ToText(lastLine - 1, lastLine - 1);

                if (curMail.Type == MailType.NormalMail)
                {
                    mailsDownloaded.Add(curMail);

                    if (this.ItemDownloadedEventHandler != null)
                        this.ItemDownloadedEventHandler(this, new ItemDownloadedEventArgs(curMail));

                    CurState = State.ReadMail;
                    Send(cmd_Up);
                }
                else
                {
                    CurState = State.BackToMailbox;
                    Send(cmd_Left);
                }
            }
        }

        private void OnStatePreReplyMail()
        {
            if (!(Term.Updated[lastLine] && Term.CurrentCursor.X == lastLine))
                return;

            string line = Term.GetLine(lastLine);
            if (regex_EnterContinue.Match(line).Success)
            {
				FinishCurrentTask(false, 
                    new InvalidOperationException("Unable to send mail"),
                    false);
                CurState = State.ToMainMenu;

                Send("\rqq" + configure.MailboxToMainMenu);
            }
            else if (regex_Reply.Match(line).Success)
            {
				string cmd = (ActiveTask as ReplyMailTask).ReplyCommand + "qq" + configure.MailboxToMainMenu;
                CurState = State.ToMainMenu;
                Send(cmd);

				FinishCurrentTask(true, null, false);
            }
        }

        private void OnStateToMainMenu()
        {
            if (regex_StatusBar.Match(Term.GetLine(lastLine)).Success)
            {
                string topLine = Term.GetLine(0);
                if (topLine.Contains("主选单") && Term.CurrentCursor.X == 1 && Term.Updated[Term.RowNum - 2])
                {
                    CurState = State.MainMenu;
                    OperateOnMainMenu();
                    return;
                }                
            }
        }

        private void OnStateSetMessageOption()
        {
            if (!(Term.Updated[lastLine] && Term.Updated[lastLine - 2] && Term.Updated[lastLine-1]))
                return;

            Match fromAll = regex_AcceptMsgsFromAll.Match(Term.GetLine(lastLine - 2));
            Match fromFriends = regex_AcceptMsgsFromAFriends.Match(Term.GetLine(6));

            if (!(fromAll.Success && fromFriends.Success))
                System.Diagnostics.Debug.WriteLine("SetMsgOptFailed");

            Debug.Assert(fromAll.Success && fromFriends.Success);
            
                string cmd = "";

                bool fromAllOn = fromAll.Groups[1].Value != "OFF";
                bool fromFriendsOn = fromFriends.Groups[1].Value != "OFF";

                SetMessageOptionTask task = ActiveTask as SetMessageOptionTask;
                if (fromAllOn != task.AcceptMessages)
                    cmd += "p\r";

                if (fromFriendsOn != task.AcceptMessages)
                    cmd += "q\r";

                Match msgBoard = regex_MessageBoard.Match(Term.GetLine(12));
                if (msgBoard.Success && "ON".Equals(msgBoard.Groups[1].Value))
                    cmd += "w\r";

                cmd += "`\r\re\r";

                CurState = State.ToMainMenu;
                Send(cmd);

                FinishCurrentTask(true, null, false);
            
        }

		private void OnStateSetMailboxOption()
		{
			if (!(Term.Updated[lastLine] && Term.Updated[2]))
				return;

			Match m = regex_SaveSentMailsToOutbox.Match(Term.GetLine(2));
			if (!m.Success)
				return;

			string cmd = "";
			if ("ON" != m.Groups[1].Value)
				cmd += "a\r";

			cmd += "d\re";

			CurState = State.ToMainMenu;
			Send(cmd);

			FinishCurrentTask(true, null, false);
		}


        private void JudgeMailType()
        {
            Match match_sender = regex_Mail_Sender.Match(Term.GetLine(0));
            Match match_title = regex_Post_Title.Match(Term.GetLine(1));
            Match match_time = regex_Post_Time.Match(Term.GetLine(2));

            if (match_sender.Success && match_title.Success && match_time.Success)
            {
                curMail = new Mail(MailType.NormalMail);
                curMail.Host = this.Host;
                curMail.Sender = match_sender.Groups[1].Value;
                curMail.Title = match_title.Groups[1].Value.TrimEnd();
                if (curMail.Title == "")
                    curMail.Title = " ";
                curMail.SendTime = DateTime.ParseExact(match_time.Groups[2].Value, dateTimeFormat,
                formatProviderForDateTime, System.Globalization.DateTimeStyles.AllowInnerWhite);                
            }
            else if (IsPostValid())
            {
                curMail = new Mail(MailType.ForwardedPost);
                curMail.Host = this.Host;
            }
            else
            {
                curMail = new Mail(MailType.Message);
                curMail.Host = this.Host;
            }

            curMail.Receiver = this.id;
        }

        private void CheckState()
        {
            if (null != needUpdate)
            {
                foreach (int i in needUpdate)
                {
                    int ind = i;
                    if (i < 0)
                        ind += Term.RowNum;

                    if (!Term.Updated[ind])
                        return;
                }

                needUpdate = null;
            }

            

            switch (this.CurState)
            {
                case State.Login:
                    {
                        OnStateLogin();
                    }
                    break;
                    
                case State.EnteringBoard:
                    {
                        OnStateEnteringBoard();
                    }
                    break;

                case State.PreCreatePost:
                    {
                        OnStatePreCreatePost();
                    }
                    break;

                case State.EnteringHelpPage:
                    {
                        OnStateEnteringHelpPage();
                    }
                    break;

                case State.EnteringSearchMode:
                    {
                        OnStateEnteringSearchMode();
                    }
                    break;

                case State.SearchMode:
                    {
                        OnStateSearchMode();
                    }
                    break;

                case State.ReadPost:
                    {
                        OnStateReadPost();
                    }
                    break;

                case State.ToBoardAfterWrongPost:
                    {
                        OnStateToBoardAfterWrongPost();
                    }
                    break;

                case State.ContinueReadPost:
                    {
                        OnStateContinueReadPost();
                    }
                    break;

                case State.PreReplyPost:
                    OnStatePreReplyPost();
                    break;

                case State.PreSendMail:
                    OnStatePreSendMail();
                    break;

                case State.EnteringMailbox:
                    OnStateEnteringMailbox();
                    break;

                case State.ReadMail:
                    OnStateReadMail();
                    break;

                case State.ContinueReadMail:
                    OnStateContinueReadMail();
                    break;

                case State.BackToMailbox:
                    OnStateBackToMailbox();
                    break;

                case State.PreReplyMail:
                    OnStatePreReplyMail();
                    break;

                case State.ToMainMenu:
                    OnStateToMainMenu();
                    break;

                case State.SetMessageOption:
                    OnStateSetMessageOption();
                    break;

				case State.SetMailboxOption:
					OnStateSetMailboxOption();
					break;
            }
            

            return;
        }

        private void OnReachMailboxTop()
        {
            if (ActiveTask.TaskType == Task.Type.DownloadMails)
            {
				FinishCurrentTask(true, null, false);
                CurState = State.ToMainMenu;

                Send("qq" + configure.MailboxToMainMenu);
            }
            else if (ActiveTask.TaskType == Task.Type.ReplyMail)
            {
				FinishCurrentTask(false, new InvalidOperationException("The mail you replied does not exist"), false);
                CurState = State.ToMainMenu;

                Send("qq" + configure.MailboxToMainMenu);
            }
            else
                OnError(this, new InvalidOperationException("Bug found in BBS Client"));
                
        }
               

        private void OnReachPostListTop()
        {            
            if (ActiveTask.TaskType == Task.Type.DownloadPosts)
            {
				FinishCurrentTask(true, null, false);                                
                CurState = State.ToMainMenu;
                                
                Send("qqq" + configure.BoardToMainMenu);
            }
            else if (ActiveTask.TaskType == Task.Type.ReplyPost)
            {
				FinishCurrentTask(false, new InvalidOperationException("The post to reply does not exist, or has been retitled"), false);
                CurState = State.ToMainMenu;

                Send("qqq" + configure.BoardToMainMenu);
            }
            else
                OnError(this, new InvalidOperationException("Bug found in BBS Client"));
        }

        private bool IsPostValid()
        {
            curPost = new Post();
            curPost.Host = this.Host;

            string line = Term.GetLine(0);
            Match m = regex_Post_Author.Match(line);
            if (!m.Success)
            {
                curPost = null;
                return false;
            }

            curPost.Author = m.Groups[1].Value;
            curPost.Board = m.Groups[2].Value;

            line = Term.GetLine(1);
            m = regex_Post_Title.Match(line);
            if (!m.Success)
            {
                curPost = null;
                return false;
            }

            curPost.Title = m.Groups[1].Value.TrimEnd();
            if (curPost.Title == "")
                curPost.Title = " ";

            line = Term.GetLine(2);
            m = regex_Post_Time.Match(line);
            if (!m.Success)
            {
                curPost = null;
                return false;
            }
            
            curPost.PostTime = DateTime.ParseExact(m.Groups[2].Value, dateTimeFormat,
                formatProviderForDateTime, System.Globalization.DateTimeStyles.AllowInnerWhite);

            return true;
        }

        private void Send(string content)
        {
            receiveExpireTimer.Change(60000, Timeout.Infinite);

            lastSent = content;

            Term.ClearUpdateFlag();

            Monitor.Enter(communicator);
            
            communicator.Send(content);

            Monitor.Exit(communicator);
        }

        public delegate void ItemDownloadedEvent(object sender, ItemDownloadedEventArgs arg);
        public event ItemDownloadedEvent ItemDownloadedEventHandler;
    }
}
